package p.ithorns.example.validator;

import org.apache.commons.lang3.StringUtils;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.lang.reflect.Field;
import java.util.*;

/**
 * FieldAssociatedValidator
 * 字段关联校验
 *
 * @author HtL
 * @date 2025/3/13 16:04
 * @since 1.0.0
 */
public class FieldAssociatedValidator implements ConstraintValidator<FieldAssociated, Object> {

    private String target;

    private String when;

    private String then;


    @Override
    public void initialize(FieldAssociated annotation) {
        this.target = annotation.target();
        this.when = annotation.when();
        this.then = annotation.then();
    }

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {
        Map<String, Object> fieldsMap = getFieldsMap(value);
        boolean when = parseSpel(this.when, fieldsMap);
        if (when) {
            boolean rs = parseSpel(then, fieldsMap);
            if (!rs) {
                String message = context.getDefaultConstraintMessageTemplate();
                if (StringUtils.isNotBlank(message)) {
                    context.disableDefaultConstraintViolation();
                    context.buildConstraintViolationWithTemplate(message)
                            .addPropertyNode(target)
                            .addConstraintViolation();
                }
                return false;
            }
        }

        return true;
    }

    private Map<String, Object> getFieldsMap(Object obj) {
        Field[] fields = obj.getClass().getDeclaredFields();
        Map<String, Object> map = new HashMap<>(fields.length);
        for (Field field : fields) {
            field.setAccessible(true);
            String name = field.getName();
            try {
                Object value = field.get(obj);
                map.put(name, value);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
        return map;
    }

    public static boolean parseSpel(String spel, Map<String, Object> map) {
        if (StringUtils.isBlank(spel)) {
            return false;
        } else {
            ExpressionParser parser = new SpelExpressionParser();
            StandardEvaluationContext context = new StandardEvaluationContext();
            context.setVariables(map);
//            context.addPropertyAccessor(new MapAccessor());
//            context.addPropertyAccessor(new BeanFactoryAccessor());
            Expression exp = parser.parseExpression(spel);
            return Optional.ofNullable(exp.getValue(context, Boolean.class)).orElse(false);
        }
    }
}